/*-----------------------------------------------------------------------------
	The contents of this file are subject to the Mozilla Public License
	Version 1.1 (the "License"); you may not use this file except in
	compliance with the License. You may obtain a copy of the License at
	http://www.mozilla.org/MPL/
	
	Software distributed under the License is distributed on an "AS IS"
	basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
	License for the specific language governing rights and limitations
	under the License.
	
	The Original Code is Fever Framework code.
	
	The Initial Developer of the Original Code is Romain Ecarnot.
	Portions created by Initial Developer are Copyright (C) 2006
	the Initial Developer. All Rights Reserved.
	
	Contributor(s):
	
	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
-----------------------------------------------------------------------------*/

import org.aswing.geom.Dimension;

import com.bourre.data.libs.GraphicLib;
import com.bourre.data.libs.GraphicLibEvent;
import com.bourre.events.BubbleEvent;
import com.bourre.events.EventType;
import com.bourre.events.IEvent;

import fever.events.DynBubbleEvent;

import fvaswing.components.reader.abstract.FvReaderDocument;
import fvaswing.components.reader.FvPaperReader;
import fvaswing.components.reader.paper.FvPaperItem;
import fvaswing.components.reader.paper.FvPaperSelectionRange;

/**
 * FlashPaper document manager.
 *
 * @author Romain Ecarnot
 */
class fvaswing.components.reader.paper.FvFlashPaper extends FvReaderDocument
{
	//-------------------------------------------------------------------------
	// Events definition
	//-------------------------------------------------------------------------
	
	/** Broadcasted when the current page (as displayed in the toolbar) changes. */
	public static var onPageChangedEVENT : EventType = new EventType( "onPageChanged" );
	
	/** Broadcast when the current zoom level (as displayed in the toolbar) changes. */	public static var onZoomChangedEVENT : EventType = new EventType( "onZoomChanged" );
	
	/** 
	 * Broadcasted when the current text selection in the document changes. 
	 * 
	 * <p>For efficiency, the new selection is not passed as an argument; you should 
	 * call {@link #getTextSelectionRange()} to get the new selection.
	 */	public static var onSelectionEVENT : EventType = new EventType( "onSelection" );
	
	/** Broadcasted when the currently active tool (as displayed in the toolbar) changes. */	public static var onToolChangedEVENT : EventType = new EventType( "onToolChanged" );
	
	/** 
	 * Broadcasted when scrolling is enabled or disabled, typically by a call 
	 * to {@link #enableScrolling()}
	 */
	public static var onEnableScrollingEVENT : EventType = new EventType( "onEnableScrolling" );
	
	/** 
	 * Broadcasted when any aspect of the visible area of the document changes, 
	 * including zoom level and scroll position.
	 */	public static var onVisibleAreaChangedEVENT : EventType = new EventType( "onVisibleAreaChanged" );
	
	
	//-------------------------------------------------------------------------
	// Private properties
	//-------------------------------------------------------------------------
	
	private var _paper : Object;
	private var _lib : GraphicLib;
	private var _scrolled : Boolean;
	
	private var _interval : Number;
	private var _timeoutInterval : Number;
	
	
	//-------------------------------------------------------------------------
	// Public Properties
	//-------------------------------------------------------------------------
	
	/** 
	 * Timeout value before declaring loaded swf as no FlashPaper compliant. 
	 * (default is 2000 ms )
	 */
	public static var IFLASHPAPER_TIMEROUT : Number = 2000;
	
	
	//-------------------------------------------------------------------------
	// Public API
	//-------------------------------------------------------------------------
	
	/**
	 * Constructor.
	 */
	public function FvFlashPaper( reader : FvPaperReader, target : MovieClip )
	{
		super( reader );
		
		reader.addEventListener( FvPaperReader.onUIChangedEVENT, this, _updateUIElements );
		
		_lib = new GraphicLib( target, 1, false );
		_lib.setAntiCache( false );
		_lib.addEventListener( GraphicLib.onLoadInitEVENT, this, _onDocumentLoaded );		_lib.addEventListener( GraphicLib.onErrorEVENT, this, _onDocumentError );		_lib.addEventListener( GraphicLib.onTimeOutEVENT, this, _onDocumentError );		_lib.addEventListener( GraphicLib.onLoadProgressEVENT, this, _onDocumentProgress );
		
		_scrolled = true;
	}
	
	/**
	 * Returns loaded percent of current document.
	 */
	public function getPercent() : Number
	{
		return _lib.getPerCent();	
	}
	
	/**
	 * Returns a string describing the type of user interface that is included in 
	 * the document.
	 * 
	 * <p>The standard Adobe FlashPaper viewer always returns the string 
	 * "Macromedia FlashPaper Default Viewer".
	 */
	public function getViewerType() : String
	{
		return _paper.getViewerType();
	}
	
	/**
	 * Returns an integer indicating the version of the user interface code in this 
	 * particular document. 
	 */
	public function getViewerVersion() : Number
	{
		return _paper.getViewerVersion();
	}
	
	/**
	 * Returns the current page number (as displayed in the toolbar).<br />
	 * The first page is page {@code 1}.
	 */
	public function getCurrentPage() : Number
	{
		return _paper.getCurrentPage();
	}
	
	/**
	 * Sets the current page. 
	 * 
	 * <p>The view scrolls as necessary to ensure the page is visible, 
	 * but does not adjust zoom.
	 */
	public function setCurrentPage( pageNumber : Number ) : Void
	{
		_paper.setCurrentPage( pageNumber );
	}
	
	/**
	 * Returns the total number of pages in the document.
	 */
	public function getNumberOfPages() : Number
	{
		return _paper.getNumberOfPages();
	}
	
	/**
	 * Returns the total number of pages loaded (and able to be displayed) so far.
	 * 
	 * <p>This value must be between zero and {@link #getNumberOfPages()}, 
	 * inclusive.<br />
	 * When {@link #getLoadedPages()} == {@link #getNumberOfPages()}, the document is 
	 * fully loaded.
	 */
	public function getLoadedPages() : Number
	{
		return _paper.getLoadedPages();	
	}
	
	/**
	 * Simulates a user clicking the Print button. The document must be fully 
	 * loaded before you call this function. This function returns {@code false} 
	 * if the document is not fully loaded.
	 */
	public function printTheDocument() : Boolean
	{
		return _paper.printTheDocument();
	}
	
	/**
	 * Sets the current zoom level. 
	 * 
	 * <p>You can pass a number indicating a zoom percentage 
	 * (for example, 100 for a 100% view). 
	 * 
	 * <p>You can also pass the string {@code "width"} to zoom to the current 
	 * fit-to-width magnification for this document, 
	 * or pass the string {@code "page"} for the fit-to-page magnification.
	 */
	public function setCurrentZoom( percent : Object ) : Void
	{
		_paper.setCurrentZoom( percent );
	}
	
	/**
	 * Returns the current zoom level.
	 */
	public function getCurrentZoom() : Number
	{
		return _paper.getCurrentZoom();
	}
	
	/**
     * Apply a zoom in
     */
	public function zoomIn()
	{
    	_paper.zoomIn();
    }
    
    /**
     * Apply a zoom out
     */
    public function zoomOut()
    {
    	_paper.zoomOut();
    }
        
	/**
	 * Sets the display size of the document, in pixels.
	 * 
	 * <p>This function returns {@code true} if resize was successful, 
	 * {@code false} if resize was unsuccessful.
	 * 
	 * <p>This function fails if the document isn't fully 
	 * loaded.
	 */
	public function setSize( size : Dimension ) : Boolean
	{
		return _paper.setSize( size.width, size.height );
	}
	
	/**
	 * Used to prevent the user from scrolling the document.
	 * 
	 * <p>When scrolling is disabled, the following is true :
	 * <ul>
	 *   <li>The scroll bar is disabled, but visible.</li>
	 *   <li>Panning (using the pointing hand) is disabled.</li>
	 *   <li>Scrolling using the mouse wheel is disabled.</li>
	 * </ul>
	 * 
	 * @return the previous value of this flag.
	 */
	public function enableScrolling( flag : Boolean ) : Boolean
	{
		return _paper.enableScrolling( flag );
	}
	
	/**
	 * Returns the currently active tool. If no tool is active, an empty string is returned.
	 * The following are currently supported values
     * <ul>
     *   <li>"" (no tool)</li>
     *   <li>"pan" (hand tool)</li>
     *   <li>"select" (text selection tool)</li>
     * </ul>
	 */
	public function getCurrentTool() : String
	{
		return _paper.getCurrentTool();
	}
	
	/**
	 * Makes the given tool the active tool.
	 * 
	 * <p>This function returns {@code false} if the argument is invalid or 
	 * the given tool is disabled.
	 * 
	 * @see #getCurrentTool()
	 */
	public function setCurrentTool( tool : String ) : Boolean
	{
		return _paper.setCurrentTool( tool );
	}
	
	/**
	 * Returns an object describing the current text selection.<br />
	 * If no text is selected, this function returns {@code null}.
	 */
	public function getTextSelectionRange() : FvPaperSelectionRange
	{
		var o : FvPaperSelectionRange = new FvPaperSelectionRange();
		var range : Object = _paper.getTextSelectionRange();
		
		o.headPageIdx = range.headPageIdx;
  		o.headCharIdx = range.headCharIdx;
  		o.tailPageIdx = range.tailPageIdx;
  		o.tailCharIdx = range.tailCharIdx;
  		return o;
	}
	
	/**
	 * Selects the given range of text. 
	 * 
	 * <p>You may pass {@code null} to deselect all text.
	 * 
	 * <p>Invalid ranges are clipped to document ranges ( this allows you to set 
	 * a range that begins with zero and ends with a large number, 
	 * such as 999999, to select the entire document ).
	 * 
	 * <p>If skipBroadcast is {@code false} (or omitted), {@link #onVisibleAreaChangedEVENT} 
	 * is broadcast to listeners as necessary.<br /> 
	 * If skipBroadcast is {@code true}, {@link #onVisibleAreaChangedEVENT} is never 
	 * broadcast to listeners.
	 */
	public function setTextSelectionRange( sel : FvPaperSelectionRange, skipBroadcast : Boolean ) : Void
	{
		_paper.setTextSelectionRange( sel, skipBroadcast );
	}
	
	/**
	 * Returns the selected text as a Unicode string. 
	 * 
	 * <p>If no text is selected, an empty string is returned.
	 */
	public function getSelectedText() : String
	{
		return _paper.getSelectedText();
	}
	
	/**
	 * Returns the width of the sidebar.
	 * 
	 * <p>A document with no outline always returns zero.
	 */
	public function getSidebarWidth() : Number
	{
		return _paper.getSidebarWidth();
	}
	
	/**
	 * Sets the width of the sidebar.
	 * 
	 * <p>Pass zero to hide the sidebar completely.<br />
	 * A document with no outline ignores this call.
	 */
	public function setSidebarWidth( w : Number ) : Void
	{
		_paper.setSidebarWidth( w );
	}
	
	/**
	 * Returns any text in the Find text box as a Unicode string.
	 */
	public function getFindText() : String
	{
		return _paper.getFindText();
	}
	
	/**
	 * Sets the text in the Find text box to a Unicode string value. 
	 * 
	 * <p>This call does not perform a find operation or alter the 
	 * current selection.
	 */
	public function setFindText( s : String ) : Void
	{
		_paper.setFindText( s );	
	}
	
	/**
	 * Searches for the text in the Find text box. 
	 * 
	 * <p>The search begins from the end of the current selection.
	 * 
	 * <p>To start from the beginning of the document, call 
	 * {@link #setSelectionRange(null)} first.
	 * 
	 * <p>Returns {@code true} if text is found, {@code false} if not.<br />
	 * If text is found, the specific range can be found by 
	 * calling {@link #getTextSelectionRange()}.
	 */
	public function findNext() : Boolean
	{
		return _paper.findNext();
	}
	
	/**
	 * Opens document in a new browser window.
	 */
	public function popUpDocument() : Void
	{
		_paper.popUpWindowButton();	
	}
	
	/**
	 * Returns current {@link fvaswing.components.reader.paper.FvPaperItem}
	 */
	public function getDocument() : FvPaperItem
	{
		return FvPaperItem( _current );
	}
	
 	/**
 	 * Releases document and all event listeners subscription.
 	 */
 	public function release() : Void
 	{
 		_paper.removeListener( this );
 		_lib.release();
 	}
 	
	
	//-------------------------------------------------------------------------
	// Private implementation
	//-------------------------------------------------------------------------
	
	/**
	 * Loads passed-in {@code url} with current loading process.
	 */
	private function _load( url : String ) : Void
	{
		_paper.removeListener( this );
		_lib.load( url );
	}
	
	private function _getReader() : FvPaperReader
	{
		return FvPaperReader( parent );	
	}
	
	private function _updateUIElements( event : BubbleEvent ) : Void
	{
		var map : Object = event.getTarget();
		for( var s in map ) _paper.showUIElement( s, map[s] );
	}
	
	private function _onDocumentLoaded( event : GraphicLibEvent ) : Void
	{
		super._onDocumentLoaded( event );
		
		_interval = setInterval( this, "_checkFlashPaperInterface", 100 );
		_timeoutInterval = setInterval( this, "_handleTimeOut", IFLASHPAPER_TIMEROUT );
	}
	
	private function _checkFlashPaperInterface() : Void
	{
		_paper = _lib.getView().getIFlashPaper();
		
		if( _paper != undefined )
		{
			clearInterval( _interval );
			clearInterval( _timeoutInterval );
			
			_paper.addListener( this );
			_paper.showUIElement( "ZoomKeys", false );
			_paper.showUIElement( "Print", false );			_paper.showUIElement( "Pop", false );			
			_lib.show();
			
			_fireCompleteEvent();
		}
	}
	
	private function _handleTimeOut( ) : Void 
	{
		clearInterval( _interval );		clearInterval( _timeoutInterval );
		
		_onDocumentError();
	}
	
	private function _onDocumentError( event : IEvent ) : Void
	{
		_lib.release();
		
		super._onDocumentError( event );
	}
	

	
	//-------------------------------------------------------------------------
	// FlashPaper callbacks
	//-------------------------------------------------------------------------
	
	private function onPageChanged( newPageNumber : Number ) : Void
	{
		var e : DynBubbleEvent = new DynBubbleEvent( onPageChangedEVENT, _getReader() );
		e.newPageNumber = newPageNumber;
		broadcastEvent( e );
	}
	
	private function onZoomChanged( percent : Number ) : Void
	{
		var e : DynBubbleEvent = new DynBubbleEvent( onZoomChangedEVENT, _getReader() );
		e.percent = percent;
		broadcastEvent( e );
	}
	
	private function onSelection() : Void
	{
		_fireEventType( onSelectionEVENT );
	}
	
	private function onToolChanged( newTool : String ) : Void
	{
		var e : DynBubbleEvent = new DynBubbleEvent( onToolChangedEVENT, _getReader() );
		e.newTool = newTool;
		broadcastEvent( e );
	}
	
	private function onEnableScrolling( enable :Boolean ) : Void 
	{
		_scrolled = enable;
		
		var e : DynBubbleEvent = new DynBubbleEvent( onEnableScrollingEVENT, _getReader() );
		e.enable = enable;
		broadcastEvent( e );
	}
	
	private function onVisibleAreaChanged() : Void
	{
		_fireEventType( onVisibleAreaChangedEVENT );
	}
	
}